﻿/*
 *  Source code courtesy of the desktopWeb.CodePlex.com community project. See MS-PL license on Codeplex.com. 
 *  This is beta code not intended for a production application.
 */


//TODO: Clean References
using System;
using System.Activities;
using System.Activities.Core.Presentation;
using System.Activities.Core.Presentation.Factories;
using System.Activities.Presentation;
using System.Activities.Presentation.Metadata;
using System.Activities.Presentation.Model;
using System.Activities.Presentation.Services;
using System.Activities.Presentation.Toolbox;
using System.Activities.Presentation.Validation;
using System.Activities.Presentation.View;
using System.Activities.Statements;
using System.Activities.XamlIntegration;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using Microsoft.Win32;
using MyData.Activities;
using System.Windows.Markup;
using System.Windows.Data;
using MyData.SharePoint;
using MyData.ExtensionMethods;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Collections.ObjectModel;
using MyData.ExpressionBuilder.Interfaces;
using System.Data.SqlClient;
using MyData.Activities.SQLAzure;
using MyData.Activities.Designers;
using System.Threading;
using System.Net;
using Microsoft.VisualBasic.Activities;
using MyData.Workflow.Designer.ExpressionForms;
using MyData.ExpressionBuilder.Model;

//NEXT:
//  Ribbon
//  Property Grid (WPF or WinForms?)
namespace MyData.Workflow.Designer
{
  public partial class WorkflowDesignerControl : UserControl, IPartImportsSatisfiedNotification
  {
    public ListBox listBox;
    public SqlConnectionStringBuilder ConnectionStringBuilder { get; set; }

    private MyEditorService expressionEditorService;
    private WorkflowDesigner wd;
    private ToolboxControl toolboxControl;
    private ValidationErrorService errorService;
    private HashSet<Type> loadedToolboxActivities = new HashSet<Type>();
    private Dictionary<String, ToolboxCategory> referencedCategories = new Dictionary<string, ToolboxCategory>();
    private ToolboxCategory flowchartCategory;
    private List<ToolboxItemWrapper> flowchartActivities = new List<ToolboxItemWrapper>();

    public WorkflowDesignerControl()
    {
      
      InitializeComponent();

      //Register designers for the standard activities
      DesignerMetadata metaData = new DesignerMetadata();
      metaData.Register();

      //Toolbox
      toolboxControl = CreateToolbox();  
      toolboxArea.Child = toolboxControl;

      errorService = new ValidationErrorService(this.messageListBox);

      InitializeDesigner();
    }

    #region Workflow Designer

    private string _currentWorkflowPath = string.Empty;

    private DesignerView dView;
    public WorkflowDesigner Load(string path)
    {
      _currentWorkflowPath = path;

      try
      {
        wd.Load(_currentWorkflowPath);

        dView = wd.Context.Services.GetService<DesignerView>();
        dView.GotFocus += new RoutedEventHandler(dView_GotFocus);

        //Flow Diagram
        dView.PreviewMouseDoubleClick += new System.Windows.Input.MouseButtonEventHandler(dView_PreviewMouseDoubleClick);
      }
      catch (Exception ex)
      {
        //Handle exception in a production application
      }

      return wd;
    }

    void dView_PreviewMouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {    
      ModelItem mi = (sender as DesignerView).FocusedViewElement.ModelItem;
      if (mi.ItemType == typeof(FlowDecision))
      {       
        ConditionForm conditionForm = new ConditionForm();

        //NEXT: Assume Flowchart is in Sequence    
        ModelItem sequence = mi.GetParent(typeof(Sequence));
        if (sequence != null)
        {
          conditionForm.Variables = sequence.Properties["Variables"].ComputedValue as Collection<System.Activities.Variable>;
        }

        conditionForm.ConditionModelItem = mi;
        conditionForm.ShowDialog();
      }
    }

    private void InitializeDesigner()
    {
      //Create a designer
      wd = new WorkflowDesigner();
      wd.ModelChanged += new EventHandler(wd_ModelChanged);

      designerArea.Child = wd.View;

      this.expressionEditorService = new MyEditorService();

      //Publish the instance of MyEditorService.
      wd.Context.Services.Publish<IExpressionEditorService>(this.expressionEditorService);

      //Error service
      wd.Context.Services.Publish<IValidationErrorService>(errorService);

      //Selection
      wd.Context.Items.Subscribe<Selection>(OnItemSelected);

    }

    void wd_ModelChanged(object sender, EventArgs e)
    {     
      wd.Flush();
      this.SaveXamlFile(_currentWorkflowPath, wd.Text);

      dView.UpdateLayout();

    }
    #endregion

    #region DesignerView
    //GotFocus handles expression editor setup
    void dView_GotFocus(object sender, RoutedEventArgs e)
    {
      DesignerView dv = (DesignerView)sender;

      if (dv.FocusedViewElement != null)
      {
        Selection sel = wd.Context.Items.GetValue<Selection>(); 

        if (sel != null)
        {
          try
          {
            ActivityDesigner ad = (ActivityDesigner)sel.PrimarySelection.View;

            //This GotFocus event handler is used when dropping a new activity
            ad.GotFocus += new RoutedEventHandler(ad_GotFocus);
          }
          catch 
          { 
            //TODO:
          }
        }
      }
    }

    private void ad_GotFocus(object sender, RoutedEventArgs e)
    {
      //Custom activity designers
      ExpressionTextBox etb = (e.OriginalSource as ExpressionTextBox);

      ActivityDesigner designer = sender as ActivityDesigner;

      if (etb != null)
      {
        ModelItem expressionSource = null;
        if (etb.Expression != null)
          expressionSource = etb.Expression.Source.Value;

        SetInstanceExpression(etb.ExpressionPath(), etb.OwnerActivity, expressionSource);
 
      }

      if (designer != null)
      {
        if (designer.ModelItem.ItemType == typeof(Assign))
        {
          SetInstanceExpression("Assign", designer.ModelItem, designer.ModelItem);
        }
      }    
    }

    private void SetInstanceExpression(string path, ModelItem owneractivity, ModelItem expressionModelItem)
    {
      if (this.expressionEditorService.instance != null)
      {
        this.expressionEditorService.instance.ExpressionPath = path;
        this.expressionEditorService.instance.OwnerActivity = owneractivity;
        this.expressionEditorService.instance.ExpressionModelItem = expressionModelItem;
        this.expressionEditorService.instance.ConnectionStringBuilder = this.ConnectionStringBuilder;
      }
    }




    #endregion

    #region Event handling

    private void OnItemSelected(Selection item)
    {
      ModelItem mi = item.PrimarySelection;

      try
      {
        ActivityDesigner ad = (ActivityDesigner)mi.View;
        ad.GotFocus += new RoutedEventHandler(ad_GotFocus);
      }
      catch
      {
        //Catch unable to cast exception
      }

      if (mi != null)
      {
        if (flowchartCategory != null && wd.ContextMenu != null)
        {
          if (mi.ItemType == typeof(Flowchart))
          {
            //add the flowchart-only activities
            foreach (var tool in flowchartActivities)
            {
              if (!flowchartCategory.Tools.Contains(tool))
              {
                flowchartCategory.Tools.Add(tool);
              }
            }
          }
          else
          {
            //remove the flowchart-only activities
            foreach (var tool in flowchartActivities)
            {
              flowchartCategory.Tools.Remove(tool);
            }
          }
        }
      }
    }

    #endregion

    #region Toolbox methods

    private ToolboxControl CreateToolbox()
    {
      Dictionary<String, List<Type>> activitiesToInclude =
          new Dictionary<String, List<Type>>();

      activitiesToInclude.Add("Essential", new List<Type>
            {
                typeof(Sequence),
                typeof(If),
                typeof(Assign),
                typeof(While),
                typeof(DoWhile)
            });

      activitiesToInclude.Add("Microsoft SQL Azure", new List<Type>
            {   
                typeof(MyData.Activities.SQLAzure.ConnectionBuilderActivity),
                typeof(MyData.Activities.SQLAzure.SelectDataTableActivity)
            });

      //Next: CamlWhere should be dependent on SharePointItems visibility
      activitiesToInclude.Add("Microsoft SharePoint 2010", new List<Type>
            {   
                typeof(MyData.Activities.SharePointPermissions),
                typeof(MyData.Activities.SharePointAddItem),
                typeof(MyData.Activities.SharePointItems),
                typeof(MyData.Activities.CamlWhere)

            });

      //Next: RangePartActivitydependent on DocumentParts visibility
      activitiesToInclude.Add("Open XML (ServerContext)", new List<Type>
            {   
                typeof(MyData.Activities.OfficeXml.DocumentParts),
                typeof(MyData.Activities.OfficeXml.RangePartActivity),
                typeof(MyData.Activities.OfficeXml.ContentControlPartActivity)
            });

      activitiesToInclude.Add("View Publishers", new List<Type>
            {   
                typeof(MyData.Activities.ViewPublishers.FormattingActivity)
            });
      activitiesToInclude.Add("Forms", new List<Type>
            {   
                typeof(MyData.Activities.LoginFormActivity)
            });

      activitiesToInclude.Add("Flowchart", new List<Type>
            {
                typeof(Flowchart),
                typeof(FlowDecision),
                typeof(FlowSwitch<>)
            });


      ToolboxControl tb = new ToolboxControl();
      foreach (var category in activitiesToInclude)
      {
        ToolboxCategory cat = CreateToolboxCategory(
            category.Key, category.Value, true);
        tb.Categories.Add(cat);

        if (cat.CategoryName == "Flowchart")
        {
          flowchartCategory = cat;
          foreach (var tool in cat.Tools)
          {
            
            if (tool.Type == typeof(FlowDecision) ||
                tool.Type == typeof(FlowSwitch<>))
            {
              flowchartActivities.Add(tool);
            }
          }
        }
      }

      return tb;
    }

    private ToolboxCategory CreateToolboxCategory(
        String categoryName, List<Type> activities, Boolean isStandard)
    {
      ToolboxCategory tc = new ToolboxCategory(categoryName);

      foreach (Type activity in activities)
      {
        if (!loadedToolboxActivities.Contains(activity))
        {
          //Cleanup the name of generic activities
          String name;
          String[] nameChunks = activity.Name.Split('`');
          if (nameChunks.Length == 1)
          {
            name = activity.Name;
          }
          else
          {
            name = String.Format("{0}<>", nameChunks[0]);
          }

          //ToolboxImagesPath
          string iconName = Properties.Settings.Default.ToolboxImagesPath + name + ".png"; ;
          ToolboxItemWrapper tiw =
            new ToolboxItemWrapper(activity.FullName, activity.Assembly.FullName, iconName, name);

          tc.Add(tiw);

          if (isStandard)
          {
            loadedToolboxActivities.Add(activity);
          }
        }
      }

      return tc;
    }

    #endregion


    public void SaveXamlFile(String path, String markup)
    {
      try
      {
        using (FileStream stream = new FileStream(path, FileMode.Create))
        {
          using (StreamWriter writer = new StreamWriter(stream))
          {
            writer.Write(markup);
          }
        }
      }
      catch (Exception ex)
      {
        //Production application would handle catch
      }
    }

    public void OnImportsSatisfied(){}

    private void connectionButton_Click(object sender, RoutedEventArgs e)
    {
      LoginForm loginForm = new LoginForm();
      loginForm.DataAvailable += new LoginForm.ActivityFormEventHandler(loginForm_DataAvailable);
      loginForm.ShowDialog();
    }

    void loginForm_DataAvailable(object sender, ActivityFormEventArgs e)
    {
      if (e.NetworkCredential != null)
      {
        this.ConnectionStringBuilder = new SqlConnectionStringBuilder();
        this.ConnectionStringBuilder.UserID = (e.NetworkCredential as NetworkCredential).UserName;

        switch(e.ConnectionContext)
        {
          case ConnectionContext.IntegratedSecurity:
            {
              this.ConnectionStringBuilder.IntegratedSecurity = true;
              break;
            }
          case ConnectionContext.SqlAzure:
            {
              this.ConnectionStringBuilder.Encrypt = true;
              this.ConnectionStringBuilder.TrustServerCertificate = false;
              this.ConnectionStringBuilder.UserID = (e.NetworkCredential as NetworkCredential).UserName;
              this.ConnectionStringBuilder.Password = (e.NetworkCredential as NetworkCredential).Password;
              break;
            }
        }
      }
    }
  }
}
